﻿/*
 * ## VERIFICACAO DE FRASES E PALAVRAS PALINDROMAS ##
 */
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <string.h>
#include <time.h>
#define MAX_WORD 200
#define MAX_PHRASE 600
#define BILLION 1E9

/*
 * Funcao definida para contagem dos valores ASCII da string
 * @input: string a ser verificada;
 * @return: soma valores dos caracteres da string;
 */
int contaASCII(char palavra[]){
    int soma=0, i;

    for (i=0; i<=strlen(palavra); i++){
        soma+= (int) palavra[i];

    }
return soma;

}

/*
 * Funcao para a verificacao de Numero Primo 
 * - Utilizacao do Algoritmo Crivo Erastotenes
 * @input: string a ser verificada;
 * @return: retorna se eh primo ou nao;
 */
int ehPrimo(char palavra[]) {
    int i, j, *vetor, z=-1, *primos, valor;

    valor = contaASCII(palavra);

    vetor = (int*) malloc (valor*sizeof(int));
    primos = (int*) malloc (valor*sizeof(int));

    for(i=2; i<=valor; i++){
        vetor[i]=i;
    }

    for(i=2; i<=valor; i++){
        if(vetor[i]==i){
            primos[++z] = i;
            for (j=2*i; j<=valor; j+=i) {
                vetor[j]=0; // removendo da lista
            }
        }
    }

    if(primos[z]==valor)
    {
        //printf("%d eh primo\n", valor);	
	  
	return 1;
    }
    
return 0;
}

/*
 * Funcao para verificacao de palindromo
 * @input: palavra a ser verificada, numero de caracteres da palavra
 * @return: retorna se eh palindromo ou nao 
 */
int palindromo(char *word, int numcarac)
{
	int i,j,k,l;
	char buff[numcarac];

	// palavras com numero par de caracteres
	if(numcarac % 2 == 0)
	{
		i = (numcarac / 2) - 1;
		for(j=0; j<=i;j++)
			buff[j] = word[j];

		buff[j] = '\0';
		//faz verificacao espelhada
		for(k = j,l=j-1; k<numcarac;k++)
		{
			if(buff[l] != word[k])	//verifica o palindromo
				return 0; 
			else l--;
		}
		// caso nao encontra erro de espelhamento eh palindromo
		return 1;
	}
	// palavras com numero impar de caracteres
	else 
	{
		i = numcarac / 2;
		for(j=0; j<i;j++)
			buff[j] = word[j];

		buff[j] = '\0';
		//faz verificacao espelhada
		for(k = j+1,l=j-1; k<numcarac;k++)
		{
			if(buff[l] != word[k]) //verifica o palindromo
				return 0;
			else l--;
		}
		//caso nao encontra erro de espelhamento eh palindromo
		return 1;
	}
}

int main(int argc, char * argv[])
{
	FILE *fp;
	char word[MAX_WORD];
	char phrase[MAX_PHRASE];
	int i,j;
	int NumPhrases = 0;	// quantidade de Frases palindromos
	int NumCaracPhrase = 0;
	int numcarac = 0;	// contagem de caracteres na palavra
	int numWordPalindromo = 0;
	int totalPalindromo = 0;
	int TotalPhrases = 0;
	int count_div = 0;
	int NUM_BLOCK = 0;	// Divisao em Blocos para leitura do file
	size_t resultado;
	int extra = 0;
	int threadnumber = 0;
	long tam_file;
	long part_file;
	char *buffer;
	struct timespec time1, time2;
	
	if(argc < 3)
	{
		printf("passe o arquivo de leitura como argumento! e a quantidade de threads\n");
	}
	else
	{ 
		if(!(fp = fopen(argv[1],"r")))
		{
		  printf("erro! Impossivel abrir o arquivo\n");
		  return 0;
		}
		// Recebe o numero de threads e a quantidade de blocos a ser lido
		NUM_BLOCK = atoi(argv[2]);
		 
		 // calculo do tamanho do arquivo para alocacao dinamica em um buffer unico
		fseek(fp,0,SEEK_END);
		tam_file = ftell(fp);
		rewind(fp);
	        
		//aloca buffer com tamanho de bits do arquivo e faz a leitura
		buffer = (char*)malloc(sizeof(char)*(tam_file));
		fread(buffer,1,tam_file,fp);
		
		fclose(fp);	//fecha o arquivo

		// calculo da divisao de tamanho de bits a ser lido por threads
		part_file = tam_file/NUM_BLOCK;
		
		printf("\n\n== DADOS ==\n\n");
		printf("-----------------------\n");
		printf("Quantidade de threads= %d\n",NUM_BLOCK);
		printf("Tamanho arquivo total= %ld\n",tam_file);
		printf("Tamanho lido por cada thread= %ld\n",part_file);
		printf("-----------------------\n");
		
		// Seta o numero de threads a ser utilizado
		omp_set_num_threads(NUM_BLOCK);		
		
		/* INICIO DAS THREADS
		*  firstprivate - variaveis com valores antes de iniciar as threads
		*  private - cada thread possui unica variavel
		*  shared - variaveis compartilhadas entre threads
		*  reduction - reuni as threads
		*/
		printf("Inicio das Threads...\n\n");
		clock_gettime( CLOCK_REALTIME, &time1);
		#pragma omp parallel firstprivate(part_file,numWordPalindromo,NumPhrases) shared(buffer) private(word,phrase,numcarac,NumCaracPhrase,i) reduction(+:totalPalindromo,TotalPhrases)
		{
		    // verifica em qual posicao fo buffer a thread vai ler
		    int nextPositionBlock = part_file * omp_get_thread_num();
		      
				    numcarac=0;
				    NumCaracPhrase=0;
				    NumPhrases=0;
				    
				     // Imprime informacoes de quantidade de informacao lida em cada thread
				     //printf("nextPositionBlock = %d , nextPositionBlock+part_file=%ld\n",nextPositionBlock,nextPositionBlock+part_file);
				    for(i=nextPositionBlock; i<nextPositionBlock+part_file;i++)
				    {    
				            /*== VERIFICAO DE FRASES E PALAVRAS ==*/
				            // coloca os caracteres em letras minusculas	
					    buffer[i] = tolower(buffer[i]);
					    
					      // == VERIFICACAO DE FRASES ==
					      // verifica indicio de final de frase
					      if(buffer[i] == '.' || buffer[i] == ';' || buffer[i] == '!' || buffer[i] == '?'
							  || buffer[i] =='\0' || buffer[i] == '\n')   //end of phrase
					      {
						  // adiciona \0 no final da frase
						  phrase[NumCaracPhrase] = '\0';
						  if(NumCaracPhrase > 1 && NumCaracPhrase < MAX_PHRASE)
						  {
							  // chama funcao palindromo para verificadao da frase
							  if(palindromo(phrase,NumCaracPhrase))
							  {
								  //puts(phrase);
								  NumPhrases++; 
							  }
						  }
						  NumCaracPhrase = 0;
					      }
					      // caso seja alfanumerico e nao seja espaco nem tabulacao e nao excedeu numero maximo permitido
					      else if(isalnum(buffer[i]) && buffer[i]!=' ' && buffer[i] != '\t' && NumCaracPhrase < MAX_PHRASE)
					      {
						  phrase[NumCaracPhrase] = buffer[i];
						  NumCaracPhrase++;
					      }
					      
					      // == VERIFICAO DE PALAVRAS ==
					      
					      //verifica caractere de separacao de palavra
					      if(buffer[i] == ' ' || buffer[i]=='\n'
					      || buffer[i] == '\0' || buffer[i]=='\t')
					      {
						      // coloca \0 no final da word
						      word[numcarac] = '\0'; 
						      // Verifica se a palavra em mais de 1 caractere
						      if(numcarac > 1 && numcarac < MAX_WORD)
						      {
							      // chama funcao palindromo para verificar palindromo de palavra
							      if(palindromo(word,numcarac))
							      {
								if(ehPrimo(word))
								{
								    // puts(word);
								      numWordPalindromo++;						    
								}
							      }
						      }
						      //reinicia a contagem de caracteres da palavra
						      numcarac = 0;
					      }
					      //ignora pontos e caracteres invalidos / verifica numero maximo de caracteres
					      else if(isalnum(buffer[i]) && numcarac < MAX_WORD)
					      {
						      word[numcarac] = buffer[i];
						      numcarac++;
					      }
				    }
		    //Soma o Total de Palindromos encontrados
		    totalPalindromo += numWordPalindromo;
		    TotalPhrases += + NumPhrases;
		} //end pragma
		    free(buffer);	//libera memoria do buffer
		    buffer = NULL;
		clock_gettime( CLOCK_REALTIME, &time2);
		double accum = ( time2.tv_sec - time1.tv_sec )
  + ( time2.tv_nsec - time1.tv_nsec )
  / BILLION;
		printf("Total de Palavras = %d\n",totalPalindromo);
		printf("Total de Phrases = %d\n",TotalPhrases);
		printf("Tempo total = %lf\n",accum);
	}
	return 0;
}
